grammar plp2;

@header
{
	import java.lang.String;
}

@members
{
	Integer nivel = 0;
	String funcionActual = "";

	TablaSimbolos tablaSimbolos = new TablaSimbolos();
	Operaciones operaciones = new Operaciones();

	public void LanzarError5( MyToken t )
	{
		StringBuilder bError = new StringBuilder();
		bError.append("Error 5 (");
		bError.append(t.getFila());
		bError.append(",");
		bError.append(t.getColumna());
		bError.append("): '");
		bError.append(t.getLexema());
		bError.append("' ya existe en este ambito");

		System.err.println(bError.toString());
		System.exit (1);
	}
	
	public void LanzarError6( MyToken t )
	{
		StringBuilder bError = new StringBuilder();
		bError.append("Error 6 (");
		bError.append(t.getFila());
		bError.append(",");
		bError.append(t.getColumna());
		bError.append("): '");
		bError.append(t.getLexema());
		bError.append("' no ha sido declarado");

		System.err.println(bError.toString());
		System.exit (1);
	}
	
	public void LanzarError7( MyToken t )
	{
		StringBuilder bError = new StringBuilder();
		bError.append("Error 7 (");
		bError.append(t.getFila());
		bError.append(",");
		bError.append(t.getColumna());
		bError.append("): '");
		bError.append(t.getLexema());
		bError.append("' no es una variable");

		System.err.println(bError.toString());
		System.exit (1);
	}
	
	public void LanzarError8( MyToken t )
	{
		StringBuilder bError = new StringBuilder();
		bError.append("Error 8 (");
		bError.append(t.getFila());
		bError.append(",");
		bError.append(t.getColumna());
		bError.append("): '");
		bError.append(t.getLexema());
		bError.append("' debe ser de tipo real");

		System.err.println(bError.toString());
		System.exit (1);
	}
}

@lexer::members
{
	public void emitErrorMessage(String m)
	{
		System.err.println( m );
		System.exit (1);		
	}
}

@rulecatch 
{ 
	catch (RecognitionException re)
	{ 
		reportError(re); 
		System.exit(1); 
	} 
} 


/* SINTAX READER */

s
@init {StringBuilder salida = new StringBuilder();} :
	sp
	{
		salida.append("class TradC {\n");
		salida.append ( $sp.trad );
	}
	MAIN
	{
		tablaSimbolos.addSimbolo( new Simbolo(nivel, Simbolo.FUNCION, "main", null) );
	}
	PARI
	PARD
	{
		salida.append("\npublic static void main(String[] args) {\n");
		funcionActual="main";
	}
	bloque
	{
		salida.append( $bloque.trad );
		salida.append("\n").append(operaciones.toString());
		salida.append("}\n}");
	}
	EOF
	{
		if ( $EOF.text != "<missing EOF>" )
		{
			System.out.println(salida.toString());
		}
	};

sp
returns [String trad]
@init {StringBuilder salida = new StringBuilder(); trad = "";} : 
	(
		funcion
		{
			salida.append($funcion.trad);
		}
	)*
	{
		$trad = salida.toString();
	};

funcion
returns [String trad]
@init {StringBuilder salida = new StringBuilder();trad = "";} : 
	tipo[true]
	{
		salida.append("\npublic static ");
		salida.append( $tipo.trad );
	}
	ID
	{
		funcionActual = $ID.text;
		tablaSimbolos.addSimbolo( new Simbolo(nivel, Simbolo.FUNCION, funcionActual, null) );
		salida.append(funcionActual);
	}
	PARI
	PARD
	{
		salida.append("()");
		salida.append(" {\n");
	}
	bloque
	{
		salida.append( $bloque.trad );
		salida.append("\n");
		salida.append(operaciones.toString());
		salida.append("}\n");
		$trad = salida.toString();
	};

tipo [boolean isFuncion]
returns [String trad] 
@init {trad = "";} :
	INT
	{
		if (!isFuncion)
		{
			$trad = "  Integer";
		}
		else
		{
			$trad = "int ";
		}
	}
	|
	DOUBLE
	{
		if (!isFuncion)
		{
			$trad = "  Double";
		}
		else
		{
			$trad = "double ";
		}
	};

bloque
returns [String trad] 
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	LLAVEI
	{
		tablaSimbolos = tablaSimbolos.push();
		nivel++;
	}
	secinstr
	{
		salida.append( $secinstr.trad );
	}
	LLAVED
	{
		tablaSimbolos = tablaSimbolos.pop();
		nivel--;
		$trad = salida.toString();
	};

secinstr
returns [String trad]
@init {StringBuilder salida = new StringBuilder(); trad = "";} :
	(
		v 
		{
			salida.append( $v.trad );
		}
	|
		instr
		{
			salida.append( $instr.trad );
		}
	)*
	{
		$trad = salida.toString();
	};

v
returns [String trad] 
@init {StringBuilder salida = new StringBuilder(); MyToken idVar; String tipo; trad = "";} :
	tipo[false]
	{
		tipo = $tipo.trad;
		salida.append(tipo).append(" ");
	}
	ID
	{
		idVar = new MyToken( $ID.text, MyToken.ID, $ID.getLine(), $ID.getCharPositionInLine()+1);
	}
	PYC
	{
		Simbolo s;
		if ( tipo.equals("  Double") )
		{
			s = new Simbolo(nivel, Simbolo.REAL, idVar.getLexema(), funcionActual);
		}
		else
		{
			s = new Simbolo(nivel, Simbolo.ENTERO, idVar.getLexema(), funcionActual);
		}
		salida.append(s.toString()).append(";\n");
		$trad = salida.toString();
		if ( !tablaSimbolos.addSimbolo(s) )
		{
			LanzarError5( idVar );
		}
	};

instr
returns [String trad]
@init {StringBuilder salida = new StringBuilder(); MyToken t; Simbolo s; trad = "";} :
	ID
	{
		t = new MyToken( $ID.text, MyToken.ID, $ID.getLine(), $ID.getCharPositionInLine()+1);
		s = new Simbolo(nivel, 0, t.getLexema(), funcionActual);
		if ( !tablaSimbolos.existe(s) )
		{
			LanzarError6(t);
		}
		else
		{
			s = tablaSimbolos.getSimbolo(t.getLexema());
		}
		operaciones.addOperacion();
		operaciones.appendOperando(s);
	}
	ASIG
	{
		operaciones.appendOperador("=");
	}
	expr[s,t]
	PYC
	{
		operaciones.appendOperador(";");
	}
	|
	bloque
	{
		$trad = $bloque.trad;
	};

expr [Simbolo s, MyToken t]
@init {MyToken opsum;} :
	term[s,t]
	(
		ADDOP
		{
			opsum = new MyToken( $ADDOP.text, MyToken.OPSUM, $ADDOP.getLine(), $ADDOP.getCharPositionInLine()+1);
			operaciones.appendOperador(opsum.getLexema());
		}
		term[s,t]
	)*;

term [Simbolo s, MyToken t]
@init {MyToken opmul;} :
	factor[s,t]
	(
		MULOP
		{
			opmul = new MyToken( $MULOP.text, MyToken.OPMUL, $MULOP.getLine(), $MULOP.getCharPositionInLine()+1);
			operaciones.appendOperador(opmul.getLexema());
		}
		factor[s,t]
	)*;

factor [Simbolo s, MyToken t]
@init {MyToken factor;} :
	REAL
	{
		factor = new MyToken( $REAL.text, MyToken.REAL, $REAL.getLine(), $REAL.getCharPositionInLine()+1);
		if ( s.getTipo() == Simbolo.ENTERO )
		{
			LanzarError8(t);
		}
		operaciones.appendOperando( new Simbolo(0, Simbolo.DOUBLE, factor.getLexema(), "") );
	}
	|
	ENTERO
	{
		factor = new MyToken( $ENTERO.text, MyToken.ENTERO, $ENTERO.getLine(), $ENTERO.getCharPositionInLine()+1);
		operaciones.appendOperando( new Simbolo(0, Simbolo.INT, factor.getLexema(), "") );
	}
	|
	ID
	{
		MyToken varId = new MyToken( $ID.text, MyToken.ID, $ID.getLine(), $ID.getCharPositionInLine()+1);
		Simbolo sId = new Simbolo(nivel, 0, varId.getLexema(), funcionActual) ;
		if ( !tablaSimbolos.existe(sId) )
		{
			LanzarError6( varId );
		}
		sId = tablaSimbolos.getSimbolo(sId.getNombre());
		if ( tablaSimbolos.isFuncion(sId) )
		{
			LanzarError7(varId);
		}
		if ( sId.getTipo() == Simbolo.REAL && s.getTipo() == Simbolo.ENTERO )
		{
			LanzarError8(t);
		}
		operaciones.appendOperando( sId );
	};


/* Analizador léxico: */ 

PARI		:		'(';
PARD		:		')';
MULOP		:		('*' | '/' );
ADDOP		:		('+' | '-' );
PYC			:		';';
ASIG		:		'=';
DOUBLE		:		'double';
INT			:		'int';
MAIN		:		'main';
LLAVEI		:	 	'{';
LLAVED		:		'}';
ENTERO     	:    	('0'..'9')+ ; 
ID      	:    	('a'..'z'|'A'..'Z')('a'..'z'|'A'..'Z'|'0'..'9')* ; 
REAL		:		('0'..'9')+'.'('0'..'9')+;
NEWLINE		:		('\r'? '\n'|' '|'\t')+  {skip();}; 
COMENTARIO	:	    '/*' .* '*/' {skip();};

